package io.gitee.macxiang.sql.wapper;

import java.util.ArrayList;
import java.util.List;

import io.gitee.macxiang.utils.SQL;
import io.gitee.macxiang.utils.utils;

/** 条件构造器,类似lambda表达式,用于生成SQL的WHERE条件语句 */
public class BaseWhere {
  public final List<String> list = new ArrayList<>();

  /** 生成SQL的WHERE条件语句
   * @return WHERE语句; 如果条件为空则返回 ""
   */
  public String getSql(String tableName, boolean deleteFrist) {
    String ret = String.join("", list)
    // .replaceAll("^\\s*\\b(AND|OR)\\b\\s*", "") // 删除 首个 条件逻辑
    // .replaceAll("#tableName#", tableName) // 替换表名
    // .replaceAll("`id`", "id") // id 不能以 `id` 方式查询.否则报错
    ;
    if (deleteFrist) {
      ret = ret.replaceAll("^\\s*\\b(AND|OR)\\b\\s*", "");
    }
    list.clear();
    if (utils.isEmpty(ret)) {
      ret = "true";
    }
    if (!utils.isEmpty(tableName)) {
      ret = ret.replaceAll("#tableName#", tableName); // 替换表名
    }
    return ret;
  }

  /** 生成SQL的WHERE条件语句
   * @return WHERE语句; 如果条件为空则返回 ""
   */
  public String getSql(String tableName) {
    return getSql(tableName, true);
  }

  /** 清空所有条件 */
  public void clear() {
    list.clear();
  }

  /**设置条件组; 相当于增加了一组括号
  * @param callback  回调函数,接受一个Where对象, 可在这个Where对象中设置括号里的条件
  * @param isOr      与上一条件连接的逻辑,是否是OR连接;默认AND连接
  */
  public <T> void group(java.util.function.Consumer<T> callback, T callBackParam, Boolean isOr) {
    if (callback != null) {
      List<String> t = new ArrayList<>(list);
      list.clear();
      callback.accept(callBackParam);
      String ret = String.format("%s(%s)", logic(isOr), getSql(null));
      list.addAll(t);
      if (!utils.isEmpty(ret)) {
        list.add(ret);
      }
    }
  }

  protected String logic(Boolean isOr) {
    return Boolean.TRUE.equals(isOr) ? " OR " : " AND ";
  }

  /**简单添加规则
   * @param isOr  与上一条件连接的逻辑
   * @param logic 连接符: = > < <=等
   * @param value 右值;最终会转化为String,并包含在单引号'内'
   */
  private void setRule(String fieldName, String logic, Object value, Boolean isOr) {
    String s = value == null ? "''" : value.toString();
    if (!utils.isEmpty(logic)) {
      String rule = String.format("%s(#tableName#.%s %s %s)",
          logic(isOr), fieldName, logic, s);
      list.add(rule);
    }
  }

  /**设置单个字段的条件: IS NULL
   * @param fieldName 字段名或sql比较的左值
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void isNull(String fieldName, Boolean isOr) {
    setWhere(String.format("(#tableName#.%s IS NULL)", fieldName), isOr);
  }

  /**设置单个字段的条件: IS NOT NULL
   * @param fieldName 字段名或sql比较的左值
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void isNotNull(String fieldName, Boolean isOr) {
    setWhere(String.format("(#tableName#.%s IS NOT NULL)", fieldName), isOr);
  }

  /**设置单个字段的条件: =
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void eq(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, "=", value, isOr);
  }

  /**设置单个字段的条件: 不等于(&lt;&gt;)
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void ne(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, "<>", value, isOr);
  }

  /**设置单个字段的条件: 大于(&gt;)
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void gt(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, ">", value, isOr);
  }

  /**设置单个字段的条件: 大于等于(&gt;=)
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void ge(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, ">=", value, isOr);
  }

  /**设置单个字段的条件: 小于(&lt;)
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void lt(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, "<", value, isOr);
  }

  /**设置单个字段的条件: 小于等于(&lt;=)
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void le(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, "<=", value, isOr);
  }

  /**设置单个字段的条件: BETWEEN
   * @param fieldName 字段名或sql比较的左值
   * @param start 起始值; 值为null时 此条件无效
   * @param end   结束值; 值为null时 此条件无效
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void between(String fieldName, Object start, Object end, Boolean isOr) {
    String a = SQL.safeString(start, true), b = SQL.safeString(end, true);
    if (a.length() > 2 && b.length() > 2) {
      String rule = String.format("%s(#tableName#.%s BETWEEN %s AND %s)",
          logic(isOr), fieldName, a, b);
      list.add(rule);
    }
  }

  /**设置单个字段的条件: NOT BETWEEN
   * @param fieldName 字段名或sql比较的左值
   * @param start 起始值; 值为null时 此条件无效
   * @param end   结束值; 值为null时 此条件无效
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void notBetween(String fieldName, Object start, Object end, Boolean isOr) {
    String a = SQL.safeString(start), b = SQL.safeString(end);
    if (a.length() > 0 && b.length() > 0) {
      String rule = String.format("%s(#tableName#.%s NOT BETWEEN %s AND %s)",
          logic(isOr), fieldName, a, b);
      list.add(rule);
    }
  }

  /**设置单个字段的条件: LIKE
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void like(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, "LIKE", value, isOr);
  }

  /**设置单个字段的条件: NOT LIKE
   * @param fieldName 字段名或sql比较的左值
   * @param value 比较的值或字段名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void notLike(String fieldName, Object value, Boolean isOr) {
    setRule(fieldName, "NOT LIKE", value, isOr);
  }

  /**设置单个字段的条件: IN
   * @param fieldName 字段名或sql比较的左值
   * @param value 数组
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void in(String fieldName, Object[] value, Boolean isOr) {
    if (value != null) {
      List<String> a = new ArrayList<>();
      for (Object e : value) {
        a.add(SQL.safeString(e, true));
      }
      if (a.size() > 0) {
        String rule = String.format("%s(#tableName#.%s IN (%s))",
            logic(isOr), fieldName, String.join(", ", a));
        list.add(rule);
      }
    }
  }

  /**设置单个字段的条件: NOT IN
   * @param fieldName 字段名或sql比较的左值
   * @param value 数组
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void notIn(String fieldName, Object[] value, Boolean isOr) {
    if (value != null) {
      List<String> a = new ArrayList<>();
      for (Object e : value) {
        a.add(SQL.safeString(e, true));
      }
      if (a.size() > 0) {
        String rule = String.format("%s(#tableName#.%s NOT IN (%s))",
            logic(isOr), fieldName, String.join(", ", a));
        list.add(rule);
      }
    }
  }

  /**设置单个字段的条件: IN
   * 与in方法不同的是 接受参数sql为SQL语句
   * @param fieldName 字段名或sql比较的左值
   * @param sql SQL语句
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void inSql(String fieldName, String sql, Boolean isOr) {
    if (!utils.isEmpty(sql)) {
      String rule = String.format("%s(#tableName#.%s IN (%s))",
          logic(isOr), fieldName, sql);
      list.add(rule);
    }
  }

  /**设置单个字段的条件: NOT IN
   * 与notIn方法不同的是 接受参数sql为SQL语句
   * @param fieldName 字段名或sql比较的左值
   * @param sql SQL语句
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void notInSql(String fieldName, String sql, Boolean isOr) {
    if (!utils.isEmpty(sql)) {
      String rule = String.format("#tableName#.%s NOT IN (%s)",
          logic(isOr), fieldName, sql);
      list.add(rule);
    }
  }

  /**设置SQL语句条件
   * @param sql   要设置的条件sql;例如: id=abc;
   * 如果设置了别名,那么field必须跟上别名,或者加入前缀 #tableName#.;更改后为 #tableName#.`id`=abc;
   * 其中abc也可以是个表达式,如果是等于另一个表的值,也需要跟上其他表的别名;
   * @param isOr  与上一条件连接的逻辑,是否是OR连接;默认AND连接
   */
  public void setWhere(String sql, Boolean isOr) {
    if (!utils.isEmpty(sql)) {
      list.add(logic(isOr) + sql);
    }
  }

}
